#include "Base64.h"
#include <stdio.h>
#include "utiles.h"

extern int readEncode(int fd,char **buffer);

//en la tabla agrego el caracter '=' 
TBase64 Base64[] = { { 0, 'A' }, { 1, 'B' }, { 2, 'C' }, { 3, 'D' }, { 4, 'E' },
		{ 5, 'F' }, { 6, 'G' }, { 7, 'H' }, { 8, 'I' }, { 9, 'J' }, { 10, 'K' },
		{ 11, 'L' }, { 12, 'M' }, { 13, 'N' }, { 14, 'O' }, { 15, 'P' }, { 16,
				'Q' }, { 17, 'R' }, { 18, 'S' }, { 19, 'T' }, { 20, 'U' }, { 21,
				'V' }, { 22, 'W' }, { 23, 'X' }, { 24, 'Y' }, { 25, 'Z' }, { 26,
				'a' }, { 27, 'b' }, { 28, 'c' }, { 29, 'd' }, { 30, 'e' }, { 31,
				'f' }, { 32, 'g' }, { 33, 'h' }, { 34, 'i' }, { 35, 'j' }, { 36,
				'k' }, { 37, 'l' }, { 38, 'm' }, { 39, 'n' }, { 40, 'o' }, { 41,
				'p' }, { 42, 'q' }, { 43, 'r' }, { 44, 's' }, { 45, 't' }, { 46,
				'u' }, { 47, 'v' }, { 48, 'w' }, { 49, 'x' }, { 50, 'y' }, { 51,
				'z' }, { 52, '0' }, { 53, '1' }, { 54, '2' }, { 55, '3' }, { 56,
				'4' }, { 57, '5' }, { 58, '6' }, { 59, '7' }, { 60, '8' }, { 61,
				'9' }, { 62, '+' }, { 63, '/' }, { 64, '=' } };

/**********************************
 *Declaraciones			  *
 *-------------			  *
 ***********************************/
int
base64_get_value(char ascii);

int
b64_ascii_1(char c);

int
b64_ascii_2(char c1, char c2);

int
b64_ascii_3(char c1, char c2);

int
b64_ascii_4(char c);

int
b64_ascii_3_filled(char c);

int
b64_ascii_2_filled(char c);
/*************************************************/

int b64_encode(FILE* infd, FILE* outfd) {
	int i = 0;
	int continuar = 1;
	short maxima_linea = 0;
	int errno = 0;

	//Hago la lectura del infd
	char buffer[5],*binary_data;
	binary_data=NULL;


	while(continuar){

		int fd = fileno( infd );
		int LenG = readEncode(fd, &binary_data);

		if (LenG == EOF) {
			continuar = 0;
			continue;
		}

		for (i = 0; i < B64_MAX_ASCII + 1; i++)
			buffer[i] = '\0';

		switch (LenG) {
		case 1:
			buffer[0] = b64_ascii_1(binary_data[0]);
			buffer[1] = b64_ascii_2_filled(binary_data[0]);
			buffer[2] = Base64[B64_PADDING].ASCII;
			buffer[3] = Base64[B64_PADDING].ASCII;
			break;
		case 2:
			buffer[0] = b64_ascii_1(binary_data[0]);
			buffer[1] = b64_ascii_2(binary_data[0], binary_data[1]);
			buffer[2] = b64_ascii_3_filled(binary_data[1]);
			buffer[3] = Base64[B64_PADDING].ASCII;
			break;
		case 3:
			buffer[0] = b64_ascii_1(binary_data[0]);
			buffer[1] = b64_ascii_2(binary_data[0], binary_data[1]);
			buffer[2] = b64_ascii_3(binary_data[1], binary_data[2]);
			buffer[3] = b64_ascii_4(binary_data[2]);
			break;
		default:
			errno = B64_LENGTH_INVAL;
			break;
		}

		if(maxima_linea == 76){
			maxima_linea = 0;
			fprintf(outfd,"%s"," ");
		}

		//bajo lo leido al outfd
		fwrite(buffer,sizeof(char) * 4,1,outfd);


		maxima_linea += 4;
	}



	return errno;
}

int b64_decode(FILE* infd, FILE* outfd) {
	int i = 0;
	int continuar = 1;
	int size_buffer = 0;
	int errno = 0;
	char byte1, byte2, byte3, byte4, aux;
	//Hago la lectura del infd
	char buffer[5],*encode_data;
	encode_data=NULL;


	while(continuar){

		int LenG = readDecode(infd, &encode_data);
		if (LenG == EOF) {
			continuar = 0;
			continue;
		}


		if (B64_MAX_ASCII == LenG) {

			size_buffer = 3;
			for (i = 0; i < B64_MAX_ASCII + 1; i++)
				buffer[i] = '\0';

			if ((byte1 = (char) base64_get_value(encode_data[0]))
					== B64_CHARACTER_INVAL)
				return B64_CHARACTER_INVAL;
			if ((byte2 = (char) base64_get_value(encode_data[1]))
					== B64_CHARACTER_INVAL)
				return B64_CHARACTER_INVAL;
			if ((byte3 = (char) base64_get_value(encode_data[2]))
					== B64_CHARACTER_INVAL)
				return B64_CHARACTER_INVAL;
			if ((byte4 = (char) base64_get_value(encode_data[3]))
					== B64_CHARACTER_INVAL)
				return B64_CHARACTER_INVAL;

			byte1 = byte1 << 2;
			byte1 = byte1 & B64_MASK_SET_2_BW;
			aux = byte2 >> 4;
			aux = aux & B64_MASK_SET_6_UP;
			buffer[0] = byte1 | aux;

			if (byte3 != 64) {
				byte2 = byte2 << 4;
				byte2 = byte2 & B64_MASK_SET_4_BW;
				aux = byte3 >> 2;
				aux = aux & B64_MASK_SET_4_UP;
				buffer[1] = byte2 | aux;
				if (byte4 != 64) {
					byte3 = byte3 << 6;
					byte3 = byte3 & B64_MASK_SET_6_BW;
					aux = byte4 & B64_MASK_SET_2_UP;
					buffer[2] = byte3 | aux;
				} else {
					buffer[2] = '\0';
					size_buffer = 2;
				}

			} else {
				buffer[1] = '\0';
				buffer[2] = '\0';
				size_buffer = 1;
			}
		} else
			errno = B64_LENGTH_INVAL;


		fwrite(buffer,sizeof(char) * size_buffer,1 ,outfd);
	}
	return errno;
}

int base64_get_value(char ascii) {
	int i = 0;
	for (i = 0; i < B64_LENGTH_TBASE64; i++)
		if (Base64[i].ASCII == ascii)
			return Base64[i].value;
	return B64_CHARACTER_INVAL;
}

int b64_ascii_1(char c) {
	char Nro;
	Nro = c >> 2;
	Nro = Nro & B64_MASK_SET_2_UP; //Obtengo el pirmer numero
	return Base64[(int) Nro].ASCII; //Obtengo el ASCII asociado al Nro1
}

int b64_ascii_2(char c1, char c2) {
	char Nro, aux;
	Nro = c1 & B64_MASK_SET_6_UP;
	Nro = Nro << 4;
	Nro = Nro & B64_MASK_SET_4_BW;
	aux = c2 >> 4;
	aux = aux & B64_MASK_SET_4_UP;
	Nro = Nro | aux; //Obtengo el segundo numero
	return Base64[(int) Nro].ASCII; //Obtengo el ASCII asociado al Nro2
}

int b64_ascii_3(char c1, char c2) {
	char Nro, aux;
	Nro = c1 & B64_MASK_SET_4_UP;
	Nro = Nro << 2;
	Nro = Nro & B64_MASK_SET_2_BW;
	aux = c2 >> 6;
	aux = aux & B64_MASK_SET_6_UP;
	Nro = Nro | aux; //Obtengo el tercer numero
	return Base64[(int) Nro].ASCII; //Obtengo el ASCII asociado al Nro3
}

int b64_ascii_4(char c) {
	char Nro;
	Nro = c & B64_MASK_SET_2_UP; //Obtengo el tercer numero
	return Base64[(int) Nro].ASCII; //Obtengo el ASCII asociado al Nro4
}

int b64_ascii_3_filled(char c) {
	char Nro;
	Nro = c << 2;
	Nro = Nro & B64_MASK_SET_2_UP;
	Nro = Nro & B64_MASK_SET_2_BW; //Obtengo el tercer numero
	return Base64[(int) Nro].ASCII; //Obtengo el ASCII asociado al Nro3 con relleno
}

int b64_ascii_2_filled(char c) {
	char Nro;
	Nro = c << 4;
	Nro = Nro & B64_MASK_SET_2_UP;
	Nro = Nro & B64_MASK_SET_4_BW; //Obtengo el tercer numero
	return Base64[(int) Nro].ASCII; //Obtengo el ASCII asociado al Nro2 con relleno
}

